package me.chyxion.spring.ext.test;

import java.util.Map;
import java.util.Map.Entry;
import javax.annotation.PostConstruct;
import org.junit.Assert;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

/**
 * Controler Test Tool
 * @version 0.1
 * @author Shaun Chyxion <br />
 * chyxion@163.com <br />
 * Jun 12, 2014 8:13:52 PM
 */
public final class ControllerTestTool {

    @Autowired
    public WebApplicationContext applicationContext;
    public MockMvc mvc;

    @PostConstruct
    public void init() {
        mvc = MockMvcBuilders.webAppContextSetup(applicationContext).build();
    }
    
    public ResultActions post(String url, Map<String, ?> params) {
    	return request(MockMvcRequestBuilders.post(url), params);
    }

    public ResultActions post(String url) {
    	return request(MockMvcRequestBuilders.post(url), null);
    }

    public ResultActions get(String url, Map<String, ?> params) {
    	return request(MockMvcRequestBuilders.get(url), params);
    }

    public ResultActions get(String url) {
    	return request(MockMvcRequestBuilders.get(url), null);
    }

    public ResultActions put(String url, Map<String, ?> params) {
    	return request(MockMvcRequestBuilders.put(url), params);
    }

    public ResultActions put(String url) {
    	return request(MockMvcRequestBuilders.put(url), null);
    }

    public ResultActions delete(String url, Map<String, ?> params) {
    	return request(MockMvcRequestBuilders.delete(url), params);
    }

    public ResultActions delete(String url) {
    	return request(MockMvcRequestBuilders.delete(url), null);
    }

    public ResultActions request(
    		MockHttpServletRequestBuilder requestBuilder, 
    		Map<String, ?> params) {
        try {
        	if (params != null && params.size() > 0) {
				for (Entry<String, ?> param : params.entrySet()) {
					Object v = param.getValue();
					if (v != null) {
						String sv = null;
						if (v instanceof String) {
							sv = (String) v;
						}
						else {
							sv = JSON.toJSONString(v);
						}
						requestBuilder.param(param.getKey(), sv);
					}
				}
        	}
        	return mvc.perform(requestBuilder);
        } 
        catch (Exception e) {
        	throw new RuntimeException(e);
        }
    }

    public ResultActions assertSuccess(ResultActions result) {
    	return assertJsonValue(result, "success", true);
    }

    public ResultActions assertFail(ResultActions result) {
    	return assertJsonValue(result, "success", false);
    }

    public ResultActions assertJsonValue(ResultActions result, final String path, final Object value) {
    	try {
    		return result.andExpect(status().isOk())
    			.andExpect(new ResultMatcher() {
					@Override
					public void match(MvcResult result) throws Exception {
						JSONObject joResp = JSON.parseObject(result.getResponse().getContentAsString());
						Object v = null;
						for (String p : path.split("\\.")) {
							if (v == null) {
								v = joResp.get(p);
							}
							else {
								v = ((JSONObject) v).get(p);
							}
                        }
						Assert.assertEquals(value, v);
					}
				});
        } 
    	catch (Exception e) {
    		throw new RuntimeException(e);
        }
    }

    public ResultActions assertForward(ResultActions result, String view) {
    	try {
    		return result.andExpect(status().isOk())
    					.andExpect(forwardedUrl(view));
        } 
    	catch (Exception e) {
    		throw new RuntimeException(e);
        }
    }

    public ResultActions print(ResultActions result) {
    	try {
	        return result.andDo(MockMvcResultHandlers.print());
        } 
    	catch (Exception e) {
    		throw new RuntimeException(e);
        }
    }
}
